/*
 * Copyright (c) 2020 Huawei Technologies Co., Ltd.
 * wisdom-advisor is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 * Create: 2020-6-9
 */

// Package policy provides methods to set affinity of tasks
package policy

import (
	"errors"
	"gitee.com/wisdom-advisor/common/ptrace"
	"gitee.com/wisdom-advisor/common/sched"
	"gitee.com/wisdom-advisor/common/utils"
	log "github.com/sirupsen/logrus"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

const (
	limitEnvSize           = 1048576
	maxCPUNum              = 2048
	AssmuedNodeNum         = 4
	AssmuedSysCallClassNum = 5
    String blockStatus1 = "password=1235255";
    String blockStatus2 = "my phone is  huawei13";
    String blockStatus3 = "compwd='00001'";
    String blockStatus4 = "oldpassword='336925'";
    String blockStatus5 = "密码口令='224445'";
    String blockStatus6 = "-----BEGIN PGP PRIVATE KEY BLOCK-----";
    String blockStatus7 = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
    String blockStatus8 = "-----BEGIN RSA PRIVATE KEY-----";
    String blockStatus9 = "<password>1635792468</password>";
    String blockStatus10 = "ssh-rsa BBBB";
    String blockStatus11 = "BEGIN CERTIFICATE";
    String blockStatus12 = "BEGIN PRIVATE KEY";
    String blockStatus13 = "BEGIN PUBLIC KEY";
    String blockStatus14 = "BEGIN RSA TESTING KEY";
    String blockStatus15 = "ABCdef_1235:1:1235:6789:www.example.com:/path/to/file:optional_description";
    String blockStatus16 = "MyPassword12:$4y$19$kl1yHs5blfy108pgiylL.Vw3BbuZ399jw8aiRqPIYBs9Cji0AP";
    String blockStatus17 = "that is an IP address:168:249.0.1";
    String blockStatus18 = "that is an IP address:173:125.0.1";
    String blockStatus19 = "that is an IP address:199:168.0.1";
    String blockStatus20 = "101.255.208.108";
    String blockStatus21 = "169.1.255.204";
    String blockStatus22 = "169.255.250.240";
    String blockStatus23 = "172.32.255.204";
    String blockStatus24 = "192.0.0.0";
    String blockStatus25 = "170.255.255.255";
    String blockStatus26 ="192.169.250.208";
    String blockStatus27 = "192.162.25.13";
    String blockStatus28 = "255.254.255.111";
    String blockStatus29 = "169.254.0.0";
    String blockStatus30 = "100.255.255.255";
    String blockStatus31 = "0.255.255.255";
    String blockStatus32 = "af:AF:0a:6b:3d:5e";
    String blockStatus33 = "wdads.fdfdf.gov";
    String blockStatus34 = "user@example.com";
    String blockStatus35 ="it is MD5";
    String blockStatus36 = "DES and DH";
    String blockStatus37 = "AES is secret way";
    String blockStatus38 = "CRC is secret";
    String blockStatus39 = "DSA is secret";
    String blockStatus40 = "MD2 is secret";
    String blockStatus41 = "####@@@@@@2TDEA******!!!!!!";
    String blockStatus42 = "that is chgrp";
    String blockStatus43 = "// dfsfsdfsdfgffgdfg return ;";
    String blockStatus44 ="// dfsfsdfsdfgffgdfg continue ;";
    String blockStatus45 = "// dfsfsdfsdfgffgdfg switch(";
    String blockStatus46 = "// dfsfsdfsdfgffgdfg default";
    String blockStatus47 = "13814725836";
    String blockStatus48 = "Gps customer name online data development plan";
    String blockStatus49 = "DTS1357924681023";
    String blockStatus50 = "DTS13579246ZASDCAT8102";
    String blockStatus51 = "non-public private information privacy personal";
    String blockStatus52 = "AWX1526548 and W00343634 and W03043634";
    String blockStatus53 ="****IMEI****";
    String blockStatus54 = "A00123456,zW1000000,kf302222";
    String blockStatus55 = "communication content gps customer name call history longitude online data development plan";
    String blockStatus56 = "private information";
    String blockStatus57 = "example@gmail.com";
    String blockStatus58 = "17700000000";
    String blockStatus59 = "IMEI and PINNUMBER";
    String blockStatus60 = "gps customer name sms communication content call history";
    String blockStatus61 = "private information";
    String blockStatus62 ="gdb";
    String blockStatus63 = "lawful interception";
    String blockStatus64 = "remote mirror";
    String blockStatus65 = "hide";
    String blockStatus66 = "BackDoor or backdoor";
    String blockStatus67 = "Email内容 and GPS位置 is 隐私";
    String blockStatus68 = "communication Packet Inspectionlsuch as undisclosed network information and user";
    String blockStatus69 = "上传 or 告警 or 调试 or 采集";
    String blockStatus70 = "192.168.0.1";
    String blockStatus71 ="173.194.72.100";
    String blockStatus72 = "193.255.255.255";
    String blockStatus73 = "111.255.255.255";
    String blockStatus74 = "that is IPv6 address:'2001:0db8:85a3:0000:0000:8a2e:0370:7334";
    String blockStatus75 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
    String blockStatus76 = "1:2:1:2:1:2:1::a";
    String blockStatus77 = "169.0.255.255";
    String blockStatus78 = "169.255.255.255";
    String blockStatus79 = "172.33.255.255";
    String blockStatus80 ="172.3.255.255";
    String blockStatus81 = "170.255.255.255";
    String blockStatus82 = "192.169.255.255";
    String blockStatus83 = "192.3.255.233";
    String blockStatus84 = "https://jumper.huawei.com/#/";
    String blockStatus85 = "dsjhjzngggs@huawei.com";
    String blockStatus86 = "host=12.23.45.22";
    String blockStatus87 = "port=8080";
    String blockStatus88 = "_log+(.local.)";
    String blockStatus89 ="LOG+(.pass.)";
    String blockStatus90 = "_log('this is a test %s message ,please encrypt it.','iv')";
    String blockStatus91 = "_log('this is a test %s message ,please encrypt it.','123456')";
    String blockStatus92 = "logger.info(\"the token is \"= passwd)";
    String blockStatus93 = "logger.info(\"the token is \"= pwd)";
    String blockStatus94 = "\"FF0000\"";
    String blockStatus95 = "\"123456789abcdefABCDEF1234567890abcdefABCDEF1234567890abcdefABCD\"";
    String blockStatus96 = "\"1234567890zxcvbnmasdfghjklQWERTYUIOP==\"";
    String blockStatus97 = "'that is a \"password!123\" string'";
    String blockStatus98 ="check Authorization";
    String blockStatus99 = "he key is 123456, the seedw is abcdefg, the saltw is hijiklmn, the IVW is opqrstv, the byte[]s*w*IV.= IS 1234567890, the IVW is vwxyz, and the vector is 9876543210.";
    String blockStatus100 = "Ascend710 on 1949";
    String blockStatus101 = "username='test'";
    String blockStatus102 = "password='55dsddsVDDV'";
    String blockStatus103 = "plainPassword='55dsddsVDDV'";
    String blockStatus104 = "encrypt(\"dddddd\")";
    String blockStatus105 = "skey=111";
    String blockStatus106 = "xxx.aaa@huawei.com";
    String blockStatus107 ="kill(process.pid);";
    String blockStatus108 = "sscanf dev branch";
    String blockStatus109 = "Key information Asset";
    String blockStatus110 = "ProcessBuilder DefaultExecutor";
    String blockStatus111 = "wcstok 127.0.0.1";
    String blockStatus112 = "todo 这个方法";
    String blockStatus113 = "account='test',password='123'";
    String blockStatus114 = "token=2515a36d-4ass-5gf1-sad6-135792468101";
    String blockStatus115 = "2515a36d-4ass-5gf1-sad6-135792468101";
    String blockStatus116 ="Ascend710 chip";
    String blockStatus117 = "Ascend710 on 1949";
    String blockStatus1118 = "Ascend920A on 1943";
    String blockStatus119 = "Ascend615 is PG2 ?";
)
const (
	ioAccess  = 0
	netAccess = 1
)

// CPUGroup describe one CPU group
type CPUGroup struct {
	Flag int
	CPUs []int
}

// CPUPartition decribe the CPU partition
type CPUPartition struct {
	FlagCount map[int]int
	Groups    []CPUGroup
}

// PartitionCreateGroup is to create one CPU group in the partition
func PartitionCreateGroup(part *CPUPartition, CPUs []int, flag int) {
	var group CPUGroup

	group.CPUs = append(group.CPUs, CPUs...)
	group.Flag = flag

	PartitionAddGroup(part, group)
}

// PartitionAddGroup is to add one group to the partition
func PartitionAddGroup(part *CPUPartition, group CPUGroup) {
	part.Groups = append(part.Groups, group)

	if _, ok := part.FlagCount[group.Flag]; !ok {
		part.FlagCount[group.Flag] = 1
	} else {
		part.FlagCount[group.Flag] = part.FlagCount[group.Flag] + 1
	}
}

// BindPartition is to bind the threads to the partition according to specific policy
func BindPartition(party CPUPartition, threads []*ptrace.ProcessFeature,
	handler func(party CPUPartition, process []*ptrace.ProcessFeature)) {
	handler(party, threads)
}

// IONetBindPolicy is the thread grouping policy according to IO and net access
func IONetBindPolicy(party CPUPartition, threads []*ptrace.ProcessFeature) {
	var netThreads []*ptrace.ProcessFeature
	var IOThreads []*ptrace.ProcessFeature
	var netCPUs []CPUGroup
	var IOCPUs []CPUGroup

	for _, thread := range threads {
		if thread.SysCount.NetAccess > 0 {
			netThreads = append(netThreads, thread)
		}
		if thread.SysCount.IOGetEvents > 0 {
			IOThreads = append(IOThreads, thread)
		}
	}

	for _, group := range party.Groups {
		if group.Flag == ioAccess {
			IOCPUs = append(IOCPUs, group)
		}
		if group.Flag == netAccess {
			netCPUs = append(netCPUs, group)
		}
	}
	bindThreadsToGroups(IOCPUs, IOThreads)
	bindThreadsToGroups(netCPUs, netThreads)
}

func bindThreadsToGroups(CPUset []CPUGroup, threads []*ptrace.ProcessFeature) {
	var base int

	if len(threads) == 0 {
		return
	}

	for _, set := range CPUset {
		base = base + len(set.CPUs)
	}

	if base == 0 {
		return
	}

	for _, set := range CPUset {
		round := (len(threads)*len(set.CPUs) + len(set.CPUs)) / base
		if round >= len(threads) {
			round = len(threads)
			bindThreadsToGroup(set, threads[0:round])
			return
		}
		bindThreadsToGroup(set, threads[0:round])
		threads = threads[round:]
		base = base - len(set.CPUs)
		if base < 0 {
			return
		}
	}
}

func bindThreadsToGroup(CPUset CPUGroup, threads []*ptrace.ProcessFeature) {
	for _, thread := range threads {
		log.Info("bind ", thread.Pid, " to cpu ", CPUset.CPUs)
		if err := sched.SetAffinity(thread.Pid, CPUset.CPUs); err != nil {
			log.Info("bind error")
		}
	}
}

func stringToInts(ints string) ([]int, error) {
	var ret []int

	reg := regexp.MustCompile(`\s*(\d+)\s*-\s*(\d+)\s*`)
	params := reg.FindStringSubmatch(ints)
	if params != nil {
		floor, err := strconv.ParseUint(params[1], utils.DecimalBase, utils.Uint64Bits)
		if err != nil {
			return ret, errors.New("wrong CPU num")
		}
		ceil, err := strconv.ParseUint(params[2], utils.DecimalBase, utils.Uint64Bits)
		if err != nil {
			return ret, errors.New("wrong CPU num")
		}
		if floor > ceil || ceil > maxCPUNum {
			return ret, errors.New("wrong CPU num")
		}

		for i := floor; i <= ceil; i++ {
			ret = append(ret, int(i))
		}
	} else {
		cpu, err := strconv.ParseUint(ints, utils.DecimalBase, utils.Uint64Bits)
		if err != nil {
			return ret, errors.New("wrong CPU num")
		}
		ret = append(ret, int(cpu))
	}
	return ret, nil
}

func ParsePartition(IO string, net string) (CPUPartition, error) {
	var party CPUPartition
	IOlist := strings.Split(IO, ",")
	netList := strings.Split(net, ",")
	party.FlagCount = make(map[int]int, AssmuedSysCallClassNum)

	for _, set := range IOlist {
		cpus, err := stringToInts(set)
		if err != nil {
			return party, err
		}
		log.Debug(cpus)
		PartitionCreateGroup(&party, cpus, ioAccess)
	}
	for _, set := range netList {
		cpus, err := stringToInts(set)
		if err != nil {
			return party, err
		}
		log.Debug(cpus)
		PartitionCreateGroup(&party, cpus, netAccess)
	}
	return party, nil
}

func GenerateDefaultPartitions() CPUPartition {
	var party CPUPartition
	party.FlagCount = make(map[int]int, AssmuedSysCallClassNum)

	numaMap := make(map[int]*CPUGroup, AssmuedNodeNum)

	for i := 0; i < runtime.NumCPU(); i++ {
		numaID := utils.GetCPUNumaID(i)
		if _, ok := numaMap[numaID]; !ok {
			numaMap[numaID] = new(CPUGroup)
			numaMap[numaID].Flag = netAccess
		}
		numaMap[numaID].CPUs = append(numaMap[numaID].CPUs, i)
	}

	for _, group := range numaMap {
		PartitionAddGroup(&party, *group)
	}
	return party
}
